<template>
  <div class="yl-virtual-scroll-list" @scroll="handleScroll">
    <div class="yl-virtual-scroll-content" :style="listStyle">
      <div
        class="yl-virtual-scroll-item"
        v-for="item in showedList"
        :key="item.index"
        :style="{
          height: itemHeight + 'px',
        }"
      >
        <slot name="item" :index="item.index">{{ item.index }}</slot>
      </div>
    </div>
  </div>
</template>

<script>
import _ from "lodash";

export default {
  name: "YlVirtualScrollList",

  components: {},

  props: {
    // 条目高度，用于计算
    // 如果条目有间隙，需要一起算上
    itemHeight: {
      type: Number,
      default: 32,
    },
    // 每页条目数量，最终显示结果是 page*4，因为多加载了前页和后2页
    pageSize: {
      type: Number,
      default: 6,
    },
    // 条目总数
    total: {
      type: Number,
      default: 0,
    },
    // 容器 paddingTop，
    // 用于有间隙的列表补充高度，可能导致页数计算误差，但是不影响，因为一般这个值很小
    paddingTop: {
      type: Number,
      default: 0,
    },
  },

  data() {
    return {
      scrollTop: 0, // 已滚动的距离
    };
  },

  computed: {
    // 内部列表，添加索引
    innerList() {
      let res = [];
      for (let i = 0; i < this.total; i++) {
        res.push({
          index: i, // 索引
          itemStart: i * this.itemHeight, // 开始位置
          itemEnd: (i + 1) * this.itemHeight, // 结束位置
        });
      }
      return res;
    },

    // 页数
    pages() {
      return Math.ceil(this.total / this.pageSize);
    },

    // 总高度
    totalHeight() {
      return this.total * this.itemHeight + this.paddingTop;
    },

    // 分页设置
    pagesSetting() {
      let res = [];

      for (let page = 0; page < this.pages; page++) {
        let startIndex = page * this.pageSize;
        let endIndex = startIndex + this.pageSize - 1;
        if (page >= this.pages - 1) {
          endIndex = this.total - 1;
        }
        let start = startIndex * this.itemHeight;
        let end = endIndex * this.itemHeight + this.itemHeight;

        res.push({
          startIndex,
          endIndex,
          start,
          end,
        });
      }

      return res;
    },

    // 当前页数
    currentPage() {
      let index = this.pagesSetting.findIndex((pageSetting) => {
        return (
          this.scrollTop >= pageSetting.start &&
          this.scrollTop < pageSetting.end
        );
      });

      if (index === -1) {
        index = 0;
      }

      return index;
    },

    // 当前展示的列表
    showedList() {
      if (this.pagesSetting.length > 0) {
        let pageStart = this.currentPage - 1;
        if (pageStart < 0) {
          pageStart = this.currentPage;
        }

        let pageEnd = this.currentPage + 2;
        if (pageEnd >= this.pages) {
          pageEnd = this.currentPage + 1;
          if (pageEnd >= this.pages) {
            pageEnd = this.currentPage;
          }
        }

        let startIndex = this.pagesSetting[pageStart].startIndex;
        let endIndex = this.pagesSetting[pageEnd].endIndex;

        return this.innerList.slice(startIndex, endIndex + 1);
      } else {
        return [];
      }
    },

    // 列表样式
    listStyle() {
      let paddingTop = 0;
      if (this.showedList[0]) {
        paddingTop = this.showedList[0].itemStart;
      }
      paddingTop += this.paddingTop;
      return {
        height: this.totalHeight + "px",
        paddingTop: paddingTop + "px",
      };
    },
  },

  watch: {},

  mounted() {},

  methods: {
    // 滚动
    handleScroll(e) {
      this.syncScrollTop(e.target.scrollTop);
    },

    // 同步 scrollTop
    syncScrollTop: _.throttle(function (scrollTop) {
      this.scrollTop = scrollTop;
    }, 100),
  },
};
</script>

<style lang="less" scoped>
.yl-virtual-scroll-list {
  height: 100%;
  overflow-y: auto;
}
</style>
